using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using Waymex.Gps;
using Waymex.Diagnostics;
using Waymex.Buffers;
using Waymex.Gps.Nmea;
using System.Globalization;


namespace Waymex.Gps
{
    /// <summary>
    /// This object is the Magellan device object though which all data objects and collections 
    /// are accessd.
    /// </summary>
    /// <example>C#
    /// <code>
    /// private void GetWaypoints()
    /// {
    ///     MagellanDevice gps = new MagellanDevice();
    ///
    ///     //Retrieve the Waypoints
    ///     WaypointCollection wpts = gps.GetWaypoints();
    /// 
    ///     //Now we have a collection of Waypoints
    ///     foreach (Waypoint wpt in wpts)
    ///     {
    ///         Console.WriteLine(wpt.Latitude);
    ///         Console.WriteLine(wpt.Longitude);
    ///     }
    ///
    ///     //Alternatively display the results in a DataGridView
    ///     dataGridView1.DataSource = wpts.ToDataSet();
    ///     dataGridView1.DataMember = "Waypoints";
    ///
    /// }
    /// </code>
    /// </example>
    public class MagellanDevice : DeviceBase, IDisposable 
	{ 
		#region Constants

		private const string MAGELLAN_MANUFACTURER_ID = "MGN";
		private const int BUFFER_READ_TIMEOUT = 2;
		private const string ERR_MSG_3000 = "Error Transferring Waypoints.";
		private const string ERR_MSG_3001 = "Error Transferring Routes.";
		private const string ERR_MSG_3002 = "Protocol Not Supported.";
		private const string ERR_MSG_3003 = "Error Transferring Position Data.";
		private const string ERR_MSG_3004 = "Error Transferring Date and Time Information.";
		private const string ERR_MSG_3005 = "Error Transferring Almanac Data.";
		private const string ERR_MSG_3006 = "Error Transferring Proximity Waypoint Data.";
		private const string ERR_MSG_3007 = "Error Transferring Track Log Data.";
		private const string ERR_MSG_3008 = "Error Transferring Product Data.";
		private const string ERR_MSG_3010 = "USB Not Supported on this Device.";
		private const string ERR_MSG_3011 = "Cannot initiate a transfer whilst a transfer is already in progress.";
        private const string ERR_MSG_3012 = "Device not Responding.";

        private const string ERR_PORT_RANGE = "The value for port must be between 1 and 256 must be specified.";
        private const string ERR_BAUD_RANGE = "The value for speed must be one of the following: 1200, 4800, 9600, 19200, 57600 or 115200.";
       

        private const string MSG_PROPERTY_NOT_SUPPORTED = "The Property is not supported in by this device.";
        private const string MSG_METHOD_NOT_SUPPORTED = "The Method is not supported in by this device.";
        private const string MSG_METHOD_NOT_SUPPORTED_IN_PE = "The Method is not supported by this version of the product.";


		#endregion

		#region Member Variables

		private SerialLink comPort = null;
		private int m_speed = 4800;
		private int m_port = 1;	//this represents com1
		private bool m_transferActive = false;
		private StringFifo m_buffer = new StringFifo();
		private string m_captureFile = "";
		private string m_captureFileFilter = "MGN";
        private ProductInfo m_prodInfo = null;

		//we cannot tell how many waypoints there are in the magellan devices and hence
		//have problems operating the progress event. The way round this is to assume
		//a number of waypoints and then replace this total once a GetWaypoints has been called.
		//Its rough and ready but seems to work reasonably well.
		private int m_waypointTotal = 75;
		private int m_trackpointTotal = 300;

		#endregion

		#region Events

		//USB Not Supported on this Device
        // .Net 1.1
        //public override event ProgressEventHandler Progress;
        //.Net 2.0
		/// <summary>
		/// The progress event fires during the data transfers to and from the GPS. 
		/// A long value of between 0 and 100 is passed to the event. 
		/// It is provided to allow a client application to display a progress 
		/// indicator during transfers.
		/// </summary>
        /// <example>C#
        /// <code><![CDATA[
        ///	// Register the event, typically in Form_Load
        ///	gps.Progress +=new EventHandler<ProgressEventArgs>(gps_Progress);
        ///
        ///	private void gps_Progress(object sender, ProgressEventArgs e)
        ///	{
        ///		// Update a progress bar. This is OK as this event
        ///		// is raised on the thread that created the Device object.
        ///		pbProgress.Value = e.Progress;
        ///	}
        /// ]]></code>
        /// </example>
        public override event EventHandler<ProgressEventArgs> Progress;

		#endregion

		#region Capture File
		/// <summary>
		/// Set this property to a valid filenamame and Megellan data will be written to this file.
		/// </summary>
		public string CaptureFile
		{
			get
			{
				return m_captureFile;
			}
			set
			{
				m_captureFile = value;
			}
		}
		/// <summary>
		/// This property can be used to limit the NMEA sentences that are written to the Capture file.
		/// For example setting the property to "RMC GLL" will ensure only the RMC and GLL sentence
		/// types are written to the file. Leaving the property empty will ensure that all sentence
		/// types are written to the file.
		/// </summary>
		private string CaptureFileFilter
		{
			get
			{
				return m_captureFileFilter;
			}
			set
			{
				m_captureFileFilter = value;
			}
		}

		private void WriteToCaptureFile(Sentence p_objSentence, string p_strFilter)
		{
			StreamWriter swText;
			string strText = "";

			try
			{
				//make sure the capture file has been entered
				if(m_captureFile.Length > 0)
				{
					// write to file here without CR as this is on source sentence;
					swText = new StreamWriter(m_captureFile,true);

					//see if there is a filter in operation
					if(p_strFilter.Length > 0)
					{
						//see if the filter matches our TypeId
						if(p_strFilter.IndexOf(p_objSentence.TypeId) >= 0)
						{
							strText = p_objSentence.ToString();
							swText.Write(strText);
						}
					}
					else
					{
						strText = p_objSentence.ToString();
						swText.Write(strText);
					}

					swText.Close();
				}
			}
			catch(Exception e)
			{
                throw new CaptureFileException(e.Message, e);
			}
		}

		
		#endregion

		#region Private Commands

		private void CommandAlmanac()
		{
			string sentence = "$PMGNCMD,ALMANAC*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandEnd()
		{
			string sentence = "$PMGNCMD,END*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandFix()
		{
			string sentence = "$PMGNCMD,FIX*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandHandOff()
		{
			string sentence = "$PMGNCMD,HANDOFF*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandOn()
		{
			string sentence = "$PMGNCMD,HANDON*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandRoute()
		{
			string sentence = "$PMGNCMD,ROUTE*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandTrack()
		{
			string sentence = "$PMGNCMD,TRACK,2*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandUnable()
		{
			string sentence = "$PMGNCMD,UNABLE*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandVersion()
		{
			string sentence = "$PMGNCMD,VERSION,2*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandWaypoint()
		{
			string sentence = "$PMGNCMD,WAYPOINT*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandTOn()
		{
			string sentence = "$PMGNCMD,TON*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandTOff()
		{
			string sentence = "$PMGNCMD,TOFF*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandNmeaOn()
		{
			string sentence = "$PMGNCMD,NMEAON*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandNmeaOff()
		{
			string sentence = "$PMGNCMD,NMEAOFF*";
			Sentence objSentence = new Sentence(sentence.ToString(CultureInfo.InvariantCulture));
			TransmitData(objSentence);
		}
		private void CommandBaud(BaudRate Speed)
		{
			StringBuilder sentence = new StringBuilder();
			sentence.Append("$PMGNCMD,");
			
			int speed = (int)Speed;
			sentence.Append(speed.ToString(CultureInfo.InvariantCulture));
			sentence.Append("*");

			Sentence objSentence = new Sentence(sentence.ToString());
			TransmitData(objSentence);
		}
		private void CommandDeleteWaypoints()
		{
			string sentence = "$PMGNCMD,DELETE,WAYPOINTS*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandDeleteRoutes()
		{
			string sentence = "$PMGNCMD,DELETE,ROUTES*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandDeleteTracks()
		{
			string sentence = "$PMGNCMD,DELETE,TRACKS*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandDeleteFixes()
		{
			string sentence = "$PMGNCMD,DELETE,FIX*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}
		private void CommandDeleteAll()
		{
			string sentence = "$PMGNCMD,DELETE,ALL*";
			Sentence objSentence = new Sentence(sentence);
			TransmitData(objSentence);
		}

		#endregion

		#region Public Properties

        /// <summary>
        /// Gets/Sets the communications port to use.
        /// </summary>
        [Obsolete("Property depreciated. Use the OpenPort() and ClosePort() methods to set a communication port.")]
        public override Port CommunicationsPort
		{
			get
			{
				return (Port)m_port;
			}
			set
			{
				m_port = (int)value;
			}
		}
        /// <summary>
        /// Sets/Gets the Baud rate of the serial connection.
        /// </summary>
        [Obsolete("Property depreciated. Use the OpenPort() and ClosePort() methods to set a communication port.")]
        public override BaudRate BaudRate
		{
			get
			{
				return (BaudRate)m_speed;
			}
			set
			{
				m_speed = (int)value;
			}
		}

		#endregion

		#region Public Methods
        /// <summary>
        /// Specifies and opens the serial communication port to use.
        /// </summary>
        /// <param name="port">The serial port to use. Values in the range 1 to 256 are accepted.</param>
        public override void OpenPort(int port)
        {
            OpenPort(port, 4800);
        }

        /// <summary>
        /// Specifies and opens the serial communication port to use at the specified speed.
        /// </summary>
        /// <param name="port">Specifies the serial port to use. Values in the range 1 to 256 are accepted.</param>
        /// <param name="speed">Specifies the baud rate to use.</param>
        public override void OpenPort(int port, int speed)
        {
            if (port < 1 || port > 256)
                throw new ArgumentOutOfRangeException(ERR_PORT_RANGE);
            m_port = port;

            if (speed == 1200 || speed == 4800 || speed == 9600 || speed == 19200 || speed == 57600 || speed == 115200)
                m_speed = speed;
            else
                throw new ArgumentOutOfRangeException(ERR_BAUD_RANGE);

        }
        /// <summary>
        /// Opens the default port, COM 1 at the default speed, 4800 baud..
        /// </summary>
        public override void OpenPort()
        {
            //set this to zero ensures usb will be selected
            OpenPort(1, 4800);
        }
        /// <summary>
        /// Closes the currently open port. This can safely be called at any time.
        /// </summary>
        public override void ClosePort()
        {
            //do nothing, this is simply here to keep users happy.
        }

		/// <summary>
		/// Constructor for the Device class.
		/// </summary>
		public MagellanDevice()
		{
			try
			{
				comPort = new SerialLink();
				comPort.OnRxSentence += new SerialLink.OnRxSentenceEventHandler(ComPort_OnRxSentence);
				Waymex.Diagnostics.TraceLog.WriteHeader();

                m_prodInfo = new ProductInfo();

			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
				throw ex;
			}
		}

//		/// <summary> 
//		/// This method cancels any transfers either to or from the connected GPS device.     
//		/// </summary>      
//		public override void CancelTransfer()
//		{
//			try
//			{
//				m_transferAbort = true;
//			}
//			catch(Exception e)
//			{
//				TraceLog.WriteError(e);
//				throw new Exceptions.GeneralException(e.Message, e);
//			}		
//		}

		/// <summary>
		/// Returns a WaypointCollection of Waypoint objects from the GPS device.
        /// </summary>
        /// <example>C#
        /// <code>
        /// private void GetWaypoints()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        ///
        ///     //Retrieve the Waypoints
        ///     WaypointCollection wpts = gps.GetWaypoints();
        /// 
        ///     //Now we have a collection of Waypoints
        ///     foreach (Waypoint wpt in wpts)
        ///     {
        ///         Console.WriteLine(wpt.Latitude);
        ///         Console.WriteLine(wpt.Longitude);
        ///     }
        ///
        ///     //Alternatively display the results in a DataGridView
        ///     dataGridView1.DataSource = wpts.ToDataSet();
        ///     dataGridView1.DataMember = "Waypoints";
        ///
        /// }
        /// </code>
        /// </example>
        public override WaypointCollection GetWaypoints()
		{
			return GetWaypoints(1, 0);
		}
        /// <param name="PercentageDivisor">
		/// Us used to modify the progress event.
		/// E.g. a value of 2 halves the progress value passed to the progress event
		/// this is used by the GetRoute method, which calls this method, to give
		/// a realistic progress value.
		/// </param>
        /// <param name="PercentageOffset"></param>
		private WaypointCollection GetWaypoints(int PercentageDivisor, int PercentageOffset)
		{
			WaypointCollection	objWaypoints		= new WaypointCollection();
			Waypoint	objWaypoint			= null;
			bool		completed			= false;
			int			waypointCount		= 0; //used to count the waypoints for progress

			if( m_transferActive )
					throw new DataTransferException(ERR_MSG_3011);

			try
			{
					if( PercentageDivisor == 0)
						PercentageDivisor = 1;

					PortOpen();

					m_buffer.Clear();
					CommandWaypoint();

					while( !completed )
					{
						Sentence objSentence = ReceiveData();


						//could get WPL or PMGNWPL
						switch( objSentence.TypeId )
						{
							case "WPL":
								break;

							case "PMGNWPL" :

						        waypointCount++;

								SentencePmgnWpl objPMGNWPL = new SentencePmgnWpl(objSentence.ToString());
					
								//create new waypoint
								objWaypoint = new Waypoint();
								objWaypoint.Latitude = objPMGNWPL.Latitude; 
								objWaypoint.Longitude = objPMGNWPL.Longitude;
								objWaypoint.Identifier = objPMGNWPL.Name;
								objWaypoint.Icon = objPMGNWPL.Icon;
								objWaypoint.Comment = objPMGNWPL.Comment;
							
							switch(objPMGNWPL.AltitudeUnits.ToUpper(CultureInfo.InvariantCulture))
							{
								case "M" :
                                    objWaypoint.Altitude = Convert.ToSingle(objPMGNWPL.Altitude, CultureInfo.InvariantCulture);
									break;
								case "F" :
									//TODO: convert to meters
                                    objWaypoint.Altitude = FeetToMeters(Convert.ToSingle(objPMGNWPL.Altitude, CultureInfo.InvariantCulture));
									break;
								default :
									//error ???
									break;
							}

								break;

							case "PMGNCMD" : //??? END ???

								//determine what the sentence actually is
								SentencePmgnCmd cmdSentence = new SentencePmgnCmd(objSentence.ToString());
								if( cmdSentence.Command.ToUpper(CultureInfo.InvariantCulture) == "END" )
								{
									objWaypoint = null;
									completed = true;
								}

						
								break;

							default :
								break;

						}
				
						//add waypoint to collection
						if( objWaypoint != null)
							objWaypoints.Add(objWaypoint);

						//work out percentage done and raise event for the caller
						if (Progress != null)
						{
							//update the progress bar assume thirty waypoints
							//as we have no way of telling
							Progress(this, new ProgressEventArgs(Limit((int)(((100.0 / m_waypointTotal) * waypointCount)/PercentageDivisor) + PercentageOffset, 0, 100)));
						
						}

						System.Windows.Forms.Application.DoEvents();

					}//end while

					//now we know how many waypoints there are so we can update the m_waypointTotal variable
					//this means that subsequent calls to GetWaypoints will update the progress more acurately
					m_waypointTotal = waypointCount;
				
					//work out percentage done and raise event for the caller
					if (Progress != null)
					{
						//update the progress bar to 100%
						//as we have no way of telling
						Progress(this, new ProgressEventArgs(Limit((int)(100/PercentageDivisor + PercentageOffset), 0, 100)));
					}
			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
				throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();

				//reset the progress bar unless the percentage divisor is in use
				if (Progress != null & PercentageDivisor == 1)
				{
					//update the progress bar assume thirty waypoints
					//as we have no way of telling
					Progress(this, new ProgressEventArgs(0));
				}
			}

			return objWaypoints;

		}
		/// <summary>
		/// Returns a AlmanacCollection of Almanac objects from the GPS Device.
        /// </summary>
        /// <example>C#
        /// <code>
        /// private void GetAlmanacs()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        ///
        ///    //Retrieve the Almanacs
        ///    AlmanacCollection almacs = gps.GetAlmanacs();
        ///
        ///    //Now we have a collection of Almanacs
        ///    foreach (Almanac almc in almacs)
        ///    {
        ///        Console.WriteLine(almc.SatelliteId);
        ///        Console.WriteLine(almc.Health);
        ///    }
        ///
        ///    //Alternatively display the results in a DataGridView
        ///    dataGridView1.DataSource = almacs.ToDataSet();
        ///    dataGridView1.DataMember = "Almanacs";
        ///
        /// }
        /// </code>
        /// </example>
        public override AlmanacCollection GetAlmanacs()
		{
			AlmanacCollection	objAlmanacs				= new AlmanacCollection();
			Almanac		objAlmanac				= null;
			bool		completed				= false;
			int			almanacTotalMessages	= 0;
			int			almanacCount		= 0;
			double		progressValue			= 0.0;

			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);

			try
			{
				PortOpen();

				m_buffer.Clear();
				CommandAlmanac();

				while( !completed )
				{

					almanacCount++;

					Sentence objSentence = ReceiveData();

					//could get WPL or PMGNWPL
					switch( objSentence.TypeId )
					{

						case "PMGNALM" :

							SentencePmgnAlm objPMGNALM = new SentencePmgnAlm(objSentence.ToString());
					
							almanacTotalMessages = objPMGNALM.TotalMessages;

							//create new waypoint
							objAlmanac = new Almanac();
							objAlmanac.SatelliteId = (short)objPMGNALM.SatelliteId; 
							objAlmanac.WeekNumber = (short)objPMGNALM.WeekNumber;
							objAlmanac.Eccentricity = (float)objPMGNALM.Eccentricity;
							objAlmanac.Health = (short)objPMGNALM.Health;
							objAlmanac.ReferenceTime = objPMGNALM.ReferenceTime;
							objAlmanac.Inclination = objPMGNALM.InclinationAngle;
							objAlmanac.RightAscensionRate = objPMGNALM.RightAscensionRate;
							objAlmanac.SqrRootSemiMajorAxis = objPMGNALM.RootOfSemiMajorAxis;
							objAlmanac.Perigee = objPMGNALM.Perigee;
							objAlmanac.MeanAnomaly = objPMGNALM.MeanAnomaly; 
							objAlmanac.ClockCorrectionAF0 = objPMGNALM.ClockCorrectionF0;
							objAlmanac.ClockCorrectionAF1 = objPMGNALM.ClockCorrectionF1;
							
							if( objPMGNALM.MessageNumber == objPMGNALM.TotalMessages )
								completed = true;
							break;

						case "PMGNCMD" : //this does not actually fire at the end of the data but may do if the transfer is cut short

							//determine what the sentence actually is
							SentencePmgnCmd cmdSentence = new SentencePmgnCmd(objSentence.ToString());
							if( cmdSentence.Command.ToUpper(CultureInfo.InvariantCulture) == "END" )
							{
								objAlmanac = null;
								completed = true;
							}

						
							break;

						default :
							//Error or could be a CHKSM if Handshaking has been left on inadvertently etc.

							break;

					}
				
					//add waypoint to collection
					if( objAlmanac != null )
						objAlmanacs.Add(objAlmanac);

					//work out percentage done and raise event for the caller
					//RaiseEvent Progress(Limit(CInt((100 / iWaypoints) * f), , 100))
					if (Progress != null)
					{
						//update the progress bar
						progressValue = 100.0 / almanacTotalMessages * almanacCount;
						Progress(this, new ProgressEventArgs(Limit((int)progressValue,0,100)));
					}

					System.Windows.Forms.Application.DoEvents();

				}//end while

			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
				throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();
				if (Progress != null)
				{
					//update the progress bar
					Progress(this, new ProgressEventArgs(0));
				}

			}

			return objAlmanacs;

		}
        /// <summary>
        /// Returns a TrackLogCollection of TrackLog objects from the GPS Device.
        /// The SplitTracklog parameter when set to true returns multi-segment tracks
        /// as separate Tracklog objects. When set to false a multi-segment track is 
        /// returned as a single Tracklog object.
        /// </summary>
        /// <example>C#
        /// <code>
        /// private void GetTracklogs()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        ///
        ///     //Retrieve the Tracklogs
        ///     TracklogCollection trks = gps.GetTracklogs();
        ///
        ///     //Now we have a collection of Tracklogs
        ///     foreach (Tracklog trk in trks)
        ///     {
        ///         Console.WriteLine(trk.Identifier);
        ///
        ///         //Each Tracklog has a collection of Trackpoints
        ///         foreach (Trackpoint tpt in trk.Trackpoints)
        ///         {
        ///             Console.WriteLine(tpt.Latitude);
        ///             Console.WriteLine(tpt.Longitude);
        ///         }
        ///     }
        ///
        ///     //Alternatively display the results in a DataGridView
        ///     dataGridView1.DataSource = trks.ToDataSet();
        ///     dataGridView1.DataMember = "Tracks";
        ///
        /// }
        /// </code>
        /// </example>
        public override TracklogCollection GetTracklogs()
		{

			TracklogCollection		objTracklogs		= new TracklogCollection();
			Tracklog		        objTracklog			= new Tracklog();
			TrackpointCollection	objTrackpoints		= null;
			Trackpoint		        objTrackpoint		= null;
			int				        trackPointCount		= 0;
			bool			        completed			= false;
			double			        progressValue		= 0.0;
			string			        tracklogName		= null;

			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);
            try
            {
                PortOpen();

                m_buffer.Clear();
                CommandTrack();

                while (!completed)
                {
                    Sentence objSentence = ReceiveData();

                    //could get WPL or PMGNWPL
                    switch (objSentence.TypeId)
                    {
                        case "PMGNTRK":

                            trackPointCount++;

                            SentencePmgnTrk objPMGNTRK = new SentencePmgnTrk(objSentence.ToString());

                            //if tracklog name is different for this trackpoint compared to the 
                            //previous trackpoint then create a new track log and add the
                            //old one to the tracklogs collection
                            if (tracklogName == null || tracklogName != objPMGNTRK.Name)
                            {
                                tracklogName = objPMGNTRK.Name;

                                //if there is an old tracklog add it to the collection
                                if (objTrackpoints != null)
                                {
                                    objTracklog.Trackpoints = objTrackpoints;
                                    objTracklogs.Add(objTracklog);
                                }

                                //create a new one and some new trackpoints
                                objTracklog = new Tracklog();
                                objTrackpoints = new TrackpointCollection();

                                objTracklog.Identifier = tracklogName;

                            }

                            //create new trackpoint
                            objTrackpoint = new Trackpoint();
                            objTrackpoint.Latitude = objPMGNTRK.Latitude;
                            objTrackpoint.Longitude = objPMGNTRK.Longitude;
                            objTrackpoint.FixDateTime = objPMGNTRK.UtcDate.Date + objPMGNTRK.UtcTime.TimeOfDay;

                            switch (objPMGNTRK.AltitudeUnits.ToUpper(CultureInfo.InvariantCulture))
                            {
                                case "M":
                                    objTrackpoint.Altitude = Convert.ToSingle(objPMGNTRK.Altitude, CultureInfo.InvariantCulture);
                                    break;
                                case "F":
                                    //TODO: convert to meters
                                    objTrackpoint.Altitude = FeetToMeters(Convert.ToSingle(objPMGNTRK.Altitude, CultureInfo.InvariantCulture));
                                    break;
                                default:
                                    //error ???
                                    break;
                            }

                            break;

                        case "PMGNCMD": //??? END ???

                            //determine what the sentence actually is
                            SentencePmgnCmd cmdSentence = new SentencePmgnCmd(objSentence.ToString());
                            if (cmdSentence.Command.ToUpper(CultureInfo.InvariantCulture) == "END")
                            {
                                objTrackpoint = null;
                                completed = true;
                            }

                            break;

                        default:
                            break;

                    }

                    //add trackpoint to collection
                    if (objTrackpoint != null)
                        objTrackpoints.Add(objTrackpoint);

                    //work out percentage done and raise event for the caller
                    if (Progress != null)
                    {
                        //update the progress bar
                        progressValue = 100.0 / m_trackpointTotal * trackPointCount;
                        Progress(this, new ProgressEventArgs(Limit((int)progressValue, 0, 100)));
                    }

                    System.Windows.Forms.Application.DoEvents();

                }// end while

                //m_trackpointTotal = trackPointCount;

                //add the remaining trackpoints to the last tracklog
                if (objTrackpoints != null)
                    objTracklog.Trackpoints = objTrackpoints;

                //add the last tracklog to the tracklogs collection
                objTracklogs.Add(objTracklog);

            }
            catch (Exception ex)
            {
                TraceLog.WriteError(ex);
                throw new DataTransferException(ex.Message, ex);
            }
			finally
			{
				PortClose();
				if (Progress != null)
				{
					//update the progress bar
					Progress(this, new ProgressEventArgs(0));
				}

			}

			return objTracklogs;

		}
		/// <summary>
		/// Returns a RouteCollection of Route objects from the GPS Device. 
        /// </summary>
        /// <example>C#
		/// <code>
        /// private void GetRoutes()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        ///
        ///     //Retrieve the Routes
        ///     RouteCollection rtes = gps.GetRoutes();
        ///
        ///     //Now we have a collection of Routes
        ///     foreach (Route rte in rtes)
        ///     {
        ///         Console.WriteLine(rte.Identifier);
        ///         Console.WriteLine(rte.Comment);
        ///
        ///         //Each Route has a collection of Waypoints
        ///         foreach (Waypoint wpt in rte.Waypoints)
        ///         {
        ///             Console.WriteLine(wpt.Latitude);
        ///             Console.WriteLine(wpt.Longitude);
        ///         }
        ///     }
        ///
        ///     //Alternatively display the results in a DataGridView
        ///     dataGridView1.DataSource = rtes.ToDataSet();
        ///     dataGridView1.DataMember = "Routes";
        ///
        /// }
		/// </code>
		/// </example>
        public override RouteCollection GetRoutes()
		{

			const int WPT_PROGRESS_DIVISOR = 2;

			RouteCollection				objRoutes				= new RouteCollection();
			Route				objRoute				= null;
			bool				completed				= false;
			int					sentenceCount			= 0;
			int					messagesInRoute			= 0;
			int					currentMessageNumber	= 0;
			int					routeNumber				= 0;
			string				routeName				= "";
			SentencePmgnRte[]	Sentences				= new SentencePmgnRte[0];
			WaypointCollection			objWaypoints;
			Waypoint			objWaypoint;
			int					waypointCount			= 0;
			double				progressValue			= 0.0;
			double				progressModifier		= 100.0/WPT_PROGRESS_DIVISOR;

			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);
			

			try
			{
				PortOpen();

				m_buffer.Clear();
				CommandRoute();
				
				//put the sentences in the array for later processing as we need access to all sentences
				//before we can process
				while( !completed )
				{


					//get the first sentence
					Sentence objSentence = ReceiveData();
                    //objSentence should never be null as the Magellan will return an END message
                    //even if there is no route data, a null received here means theres probabably no
                    //device connected
					switch( objSentence.TypeId )
					{

						case "PMGNRTE" :

							SentencePmgnRte objRTE = new SentencePmgnRte(objSentence.ToString());

							//resize the an array for the sentences
							Sentences = ReDimPreservePMGNRTE(Sentences, Sentences.Length + 1);
														
							//add each sentence to the array
							//Sentences[objRTE.MessageNumber - 1] = objRTE;
							Sentences[sentenceCount] = objRTE;

							messagesInRoute = objRTE.NumberOfMessages;
							currentMessageNumber = objRTE.MessageNumber;

							break;

						case "PMGNCMD" : //??? END ???
							
							//determine what the sentence actually is
							SentencePmgnCmd cmdSentence = new SentencePmgnCmd(objSentence.ToString());
							if( cmdSentence.Command.ToUpper(CultureInfo.InvariantCulture) == "END" )
							{
								objRoute = null;
								completed = true;
							}

							break;

						default :
							break;

					}

					//work out percentage
					if (Progress != null)
					{
						//update the progress bar only using half here
						progressValue = (100.0 / messagesInRoute * currentMessageNumber)/WPT_PROGRESS_DIVISOR;
						Progress(this, new ProgressEventArgs(Limit((int)progressValue,0,100)));
					}

					System.Windows.Forms.Application.DoEvents();
				
					sentenceCount++;

				} //end while

				//if we have had a user abort during the GetWaypoints phase we abort the whole process

				//now we have an array of route messages we need to see if we have a
				//routename as some devices dont use route name but pass a waypoint name
				//in the routename field of the sentence
				if( Sentences.Length > 0 )
				{

					//tidy up the sentences sorting out the routename issues and whether
					//icons are being used.
					routeName = SentencePmgnRte.FixRouteNameAndIcons(ref Sentences);
			
					//need to get all the waypoints so that we can associate them with the routes
					objWaypoints = GetWaypoints(WPT_PROGRESS_DIVISOR, (int)progressModifier);
	
					//ok so now we need to create the route objects
					foreach( SentencePmgnRte objRTE in Sentences )
					{
						waypointCount++;

						switch(objRTE.MessageType.ToUpper(CultureInfo.InvariantCulture))
						{
							case "C" :

								//see if the route is new 
								if( routeNumber != objRTE.RouteNumber )
								{
									//store the current route
									routeNumber = objRTE.RouteNumber;

									//see if this is the very first route, i.e. we havent created one yet
									if( objRoute != null )
									{
										//a valid route already exists so add this to the collection
										//before creating a new one
										objRoutes.Add(objRoute);
									}

									//create a new route
									objRoute = new Route();
									objRoute.Number = (short)objRTE.RouteNumber;

									//route name is a valid routename
									objRoute.Identifier = routeName;

								}
								foreach( PmgnRteRoutePoint point in objRTE.RoutePoints )
								{
									//add waypoints
									if( point.Name.Length > 0 )
									{
										objWaypoint = FindWaypoint(objWaypoints, point.Name);
								
										//waypoint found so add it to the collection
										objRoute.Waypoints.Add(objWaypoint);
									}
								}
								break;

							case "M" :
								break;
						}

						//work out percentage
						if (Progress != null)
						{
							//update the progress bar only using half here and we need to start from halfway
							progressValue = 100.0;
							Progress(this, new ProgressEventArgs(Limit((int)progressValue,0,100)));
						}

						System.Windows.Forms.Application.DoEvents();

					}//end for
				}
				//add the last route to the collection
				if( objRoute != null )
					objRoutes.Add(objRoute);
				

				if (Progress != null)
				{
					//update the progress bar
					Progress(this, new ProgressEventArgs(100));
				}

			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
				throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();

				if (Progress != null)
				{
					//reset the progress bar
					Progress(this, new ProgressEventArgs(0));
				}
			}

			return objRoutes;

		}
        /// <summary>
        /// This method returns a Product info object containing information about the connected device.
        /// </summary>
        /// <returns>ProductInfo object</returns>
        public override ProductInfo GetProductInfo()
		{
			//ProductInfo objProdInfo = new ProductInfo();

            bool		complete	= false;
			
			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);
			
			try
			{
				PortOpen();

				//initiate the Ver Command
				m_buffer.Clear();
				CommandVersion();

				//get the result from the buffer
                //set a time limit
				while( !complete )
				{

					//get the first sentence
					Sentence objSentence = ReceiveData();

					switch( objSentence.TypeId )
					{
						case "PMGNVER":
							SentencePmgnVer objPMGNVER = new SentencePmgnVer(objSentence.ToString());
				
							//populate the ProductInfo Object
							m_prodInfo.ProductId = objPMGNVER.ProductId;
                            m_prodInfo.SoftwareVersion = objPMGNVER.SoftwareVersion;
                            m_prodInfo.ProductName = "Magellan";
                            m_prodInfo.ProductDescription = objPMGNVER.ProductDescription;


                            //determine symbol set
                            switch (m_prodInfo.ProductId)
                            {
                                case 19:
                                case 23:
                                case 24:
                                case 25:
                                case 37:
                                    m_prodInfo.SymbolSet = 0;
                                    break;
                                case 18:
                                    m_prodInfo.SymbolSet = 1;
                                    break;
                                case 5:
                                    m_prodInfo.SymbolSet = 2;
                                    break;
                                case 7:
                                    m_prodInfo.SymbolSet = 3;
                                    break;
                                case 30:
                                case 35:
                                case 33:
                                case 36:
                                    m_prodInfo.SymbolSet = 4;
                                    break;
                            }


							complete = true;
							
							break;

						default :

							//some other sentence being sent by the device from a previous request
							//so ignore
							break;
					}

					System.Windows.Forms.Application.DoEvents();

				}//end while
			}
			catch(System.Exception ex)
			{
				TraceLog.WriteError(ex);
				throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();
			}
            return m_prodInfo;
		}

		/// <summary>
		/// Deletes all the stored Waypoints.
		/// </summary>
		public void DeleteWaypoints()
		{
			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);
			try
			{
				PortOpen();

				CommandDeleteWaypoints();

			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
				throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();
			}
		}
		/// <summary>
		/// Deletes all the stored Routes.
		/// </summary>
		public void DeleteRoutes()
		{
			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);

			try
			{
				PortOpen();

				CommandDeleteRoutes();
			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
				throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();
			}
		}
		/// <summary>
		/// Deletes all the stored Tracks.
		/// </summary>
		public void DeleteTracks()
		{
			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);

			try
			{
				PortOpen();
				
				CommandDeleteTracks();
			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
                throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();
			}
		}
		/// <summary>
		/// Deletes all the stored Fixes.
		/// </summary>
		public void DeleteFixes()
		{
			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);
			
			try
			{
				PortOpen();

				CommandDeleteFixes();
			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
                throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();
			}
		}
		/// <summary>
		/// Deletes all the stored Waypoints, Routes, Tracks and Fixes.
		/// </summary>
		public void DeleteAll()
		{
			if( m_transferActive )
				throw new DataTransferException(ERR_MSG_3011);

			try
			{
				PortOpen();

				CommandDeleteAll();
			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
                throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();
			}
		}
        /// <summary>
        /// Accepts a WaypointCollection of Waypoint objects and transfers them to the connected GPS device. 
        /// </summary>
        ///<example>C#
        /// <code>
        /// private void SetWaypoints()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        /// 
        ///     WaypointCollection newWpts = new WaypointCollection();
        ///     Waypoint newWpt = null;
        ///
        ///     //Create some Waypoints
        ///
        ///     //Waypoint 1
        ///     newWpt = new Waypoint();
        ///     newWpt.Identifier = "Test01";
        ///     newWpt.Latitude = 53.9608166666667;
        ///     newWpt.Longitude = -2.02225;
        ///     newWpt.Comment = "Test 1";
        ///
        ///     //Add the new waypoint to a Waypoint collection
        ///     newWpts.Add(newWpt);
        ///
        ///     //Waypoint 2
        ///     newWpt = new Waypoint();
        ///     newWpt.Identifier = "Test02";
        ///     newWpt.Latitude = 190.0;
        ///     newWpt.Longitude = 1180.0;
        ///     newWpt.Comment = "Test 2";
        ///
        ///     //Add the new Waypoint to a Waypoint collection
        ///     newWpts.Add(newWpt);
        ///
        ///     //Transfer the Waypoints to the GPS device
        ///     gps.SetWaypoints(newWpts);
        ///
        /// }
        /// </code>
        /// </example>
		public override bool SetWaypoints(WaypointCollection waypoints)
		{
			bool	result			= false;
			int		waypointCount	= 0;
			int		totalWaypoints	= 0;

			try
			{
				PortOpen();

				totalWaypoints = waypoints.Count;

				foreach(Waypoint objWaypoint in waypoints)
				{

					waypointCount++;

					SentencePmgnWpl objSentence = new SentencePmgnWpl();

					//copy the incomming waypoint to the new sentence
					objSentence.Latitude = objWaypoint.Latitude;
					objSentence.Longitude = objWaypoint.Longitude;
					objSentence.Altitude = objWaypoint.Altitude;
					objSentence.AltitudeUnits = "M";
					objSentence.Comment = CharacterSetTrim(objWaypoint.Comment);
					
					//make sure the icon is only one character
					if( objWaypoint.Icon.Length > 0 )
						objSentence.Icon = objWaypoint.Icon.Substring(0,1);
					else
						objSentence.Icon = "";

					objSentence.Name = CharacterSetTrim(objWaypoint.Identifier, 6);

					string temp = objSentence.ToString();
					SentencePmgnWpl objTemp = new SentencePmgnWpl(temp);
					
					TransmitData(objSentence);

					//work out percentage done and raise event for the caller
					if (Progress != null)
					{
						//update the progress bar assume thirty waypoints
						//as we have no way of telling
						Progress(this, new ProgressEventArgs(Limit((int)(((100.0 / totalWaypoints) * waypointCount)), 0, 100)));
					}

				}
				//work out percentage done and raise event for the caller
				if (Progress != null)
				{
					//update the progress bar assume thirty waypoints
					//as we have no way of telling
					Progress(this, new ProgressEventArgs(100));
				}

				CommandEnd();

				//if we get here all must be ok
				result = true;

			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
                throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();

				//work out percentage done and raise event for the caller
				if (Progress != null)
				{
					//update the progress bar assume thirty waypoints
					//as we have no way of telling
					Progress(this, new ProgressEventArgs(0));
				}
			}
			return result;
		}
        /// <summary>
        /// Accepts a Waypoint object and transfers it to the connected GPS device.
        /// </summary>
        /// <example>C#
        /// <code>
        /// private void SetWaypoint()
        /// {
        ///
        ///     MagellanDevice gps = new MagellanDevice();
        /// 
        ///     Waypoint newWpt = null;
        ///
        ///     //Create a Waypoint
        ///     newWpt = new Waypoint();
        ///     newWpt.Identifier = "Test01";
        ///     newWpt.Latitude = 53.9608166666667;
        ///     newWpt.Longitude = -2.02225;
        ///     newWpt.Comment = "Test 1";
        ///
        ///     //Transfer the Waypoint to the GPS device
        ///     gps.SetWaypoint(newWpt);
        ///
        /// }
        ///</code>
        ///</example>
        public override bool SetWaypoint(Waypoint waypoint)
		{
			WaypointCollection waypoints = new WaypointCollection();
			waypoints.Add(waypoint);
			return SetWaypoints(waypoints);
			
		}
		/// <summary>
		/// Accepts a Route object and transfers it to the connected GPS device.
		/// A value of True is returned if the transfer was successful.
        /// </summary>
        /// <example>C#
		/// <code>
        /// private void SetRoute()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        ///
        ///     Waypoint wpt;
        ///     Route rte;
        ///     WaypointCollection wpts;
        ///
        ///     //Create the collection of Waypoints for the Route
        ///     wpts = new WaypointCollection();
        ///
        ///     //Waypoint 1
        ///     wpt = new Waypoint();
        ///     wpt.Identifier = "RT01";
        ///     wpt.Latitude = 1.54;
        ///     wpt.Longitude = 0.2;
        ///
        ///     //Add the new Waypoint to the Waypoint Collection
        ///     wpts.Add(wpt);
        ///
        ///     //Waypoint 2
        ///     wpt = new Waypoint();
        ///     wpt.Identifier = "RT02";
        ///     wpt.Latitude = 1.53;
        ///     wpt.Longitude = 0.22;
        ///
        ///     //Add the new Waypoint to the Waypoint Collection
        ///     wpts.Add(wpt);
        ///
        ///     //Create and populate the Route
        ///     rte = new Route();
        ///     rte.Number = 2;
        ///     rte.Comment = "Hello Route";
        ///     rte.Waypoints = wpts;
        ///
        ///     //Transfer the Route to the GPS device
        ///     gps.SetRoute(rte);
        ///
        /// }
        /// </code>
		/// </example>
        public override bool SetRoute(Route route)
		{
			RouteCollection routes = new RouteCollection();
			routes.Add(route);
			return SetRoutes(routes);
		}
		/// <summary>
		/// Accepts a RouteCollection object and transfers it to the connected GPS device. 
		/// A value of True is returned if the transfer was successful.
        /// </summary>
        /// <example>C#
        /// <code>
        /// private void SetRoutes()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        /// 
        ///     Waypoint wpt;
        ///     Route rte;
        ///     RouteCollection rtes;
        ///     WaypointCollection wpts;
        ///
        ///     //Create the collection of Waypoints for the Route
        ///     wpts = new WaypointCollection();
        ///
        ///     //Waypoint 1
        ///     wpt = new Waypoint();
        ///     wpt.Identifier = "RT01";
        ///     wpt.Latitude = 1.54;
        ///     wpt.Longitude = 0.2;
        ///
        ///     //Add the new Waypoint to the Waypoint Collection
        ///     wpts.Add(wpt);
        ///
        ///     //Waypoint 2
        ///     wpt = new Waypoint();
        ///     wpt.Identifier = "RT02";
        ///     wpt.Latitude = 1.53;
        ///     wpt.Longitude = 0.22;
        ///
        ///     //Add the new Waypoint to the Waypoint Collection
        ///     wpts.Add(wpt);
        ///
        ///     //Create and populate the Route
        ///     rte = new Route();
        ///     rte.Number = 2;
        ///     rte.Comment = "Hello Route";
        ///     rte.Waypoints = wpts;
        ///
        ///     //Create a routes collection 
        ///     //Typically a single Route would be transferred using SetRoute
        ///     rtes = new RouteCollection();
        ///     rtes.Add(rte);
        ///
        ///     //Transfer the Routes to the GPS device
        ///     gps.SetRoutes(rtes);
        ///
        /// }
        /// </code>
        /// </example>
        public override bool SetRoutes(RouteCollection routes)
		{
			//send the route
			SentencePmgnRte		objSentence;
			int					totalMessages	= 0;
			bool				oddWaypoints;
			bool				onePointLeft;
			int					index;
			string[]			routePoints;


			try
			{
                //each route is sent as a single route i.e. each with a separate batch of route messages
                //rather than sending multiple routes in one batch
				foreach(Route objRoute in routes)
				{
					//get the number of waypoints
					int waypointCount = objRoute.Waypoints.Count;
				
					if( waypointCount % 2 == 1 )
					{
						//total messages is two waypoints per route message and one 'm' message
						//i.e. 31 waypoints = 16 messages + 16 'm' messages i.e. 32 messages
						//[jn] dont send m messages in this release so divide by 2
						totalMessages = (waypointCount + 1) / 2;
						oddWaypoints = true;
					}
					else
					{
						//total messages is two waypoints per route message and one 'm' message
						//i.e. 32 waypoints = 16 messages + 16 'm' messages i.e. 32 messages
						//TODO:[jn] dont send m messages in this release so divide by 2
						totalMessages = waypointCount / 2;
						oddWaypoints = false;
					}
 
					//send all the waypoints for the route so that they exist before the
					//route is created
					SetWaypoints(objRoute.Waypoints);

					PortOpen();

					//need to build up a sentence for the route
					for(int messageNumber = 1; messageNumber <= totalMessages; messageNumber++)
					{
						objSentence = new SentencePmgnRte();
						objSentence.MessageNumber = messageNumber;
						objSentence.NumberOfMessages = totalMessages;
												
						objSentence.RouteNumber = objRoute.Number;
						objSentence.RouteName = CharacterSetTrim(objRoute.Identifier, 6);

						//'c' message
						objSentence.MessageType = "c";

						//add waypoints two by two however if this is the last message 
						//and the number of waypoints is odd only one will need to be sent
						if( messageNumber == totalMessages && oddWaypoints )
							onePointLeft = true;
						else
							onePointLeft = false;

                        //send 1 or 2 as required
                        if (onePointLeft)
                        {
                            routePoints = new string[2];
                        }
                        else
                        {
                            routePoints = new string[4];
                        }

						index = messageNumber * 2 - 2;
						routePoints[0] = CharacterSetTrim(objRoute.Waypoints[index].Identifier, 6);
						routePoints[1] = objRoute.Waypoints[index].Icon;
						
						//need to sort out if odd number of waypoints
						//these dont need to be sent if this is the last message and the number
						//of waypoints is odd
						if( !onePointLeft )
						{
							routePoints[2] = CharacterSetTrim(objRoute.Waypoints[index + 1].Identifier, 6);
							routePoints[3] = objRoute.Waypoints[index + 1].Icon;
						}

                        //add the routepoints to the sentence
                        foreach (string name in routePoints)
                        {
                            PmgnRteRoutePoint point = new PmgnRteRoutePoint();
                            point.Name = name;
                            objSentence.RoutePoints.Add(point);
                        }	
						//send route message
						TransmitData(objSentence);

					}//end for

					//transmit end
					CommandEnd();

				}//end foreach
			}
			catch(Exception ex)
			{
				TraceLog.WriteError(ex);
                throw new DataTransferException(ex.Message, ex);
			}
			finally
			{
				PortClose();

				//work out percentage done and raise event for the caller
				if (Progress != null)
				{
					//update the progress bar assume thirty waypoints
					//as we have no way of telling
					Progress(this, new ProgressEventArgs(0));
				}
			}
			return true;
		}
		/// <summary>
		/// Accepts a Tracklog object and transfers it to the connected GPS device.
        /// A value of True is returned if the transfer was successful.
        /// </summary>
        /// <example>C#
        /// <code>
        /// public void SetTracklog()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        /// 
        ///     Tracklog trk;
        ///     Trackpoint tpt;
        ///
        ///     //Create a new Tracklog object
        ///     trk = new Tracklog();
        ///     trk.Identifier = "Upload";
        ///
        ///     //Create the first Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.42;
        ///     tpt.Latitude = -36.47;
        ///     tpt.NewTrack = true;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Create a second Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.43;
        ///     tpt.Latitude = -36.48;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Create the third Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.44;
        ///     tpt.Latitude = -36.49;
        ///     tpt.NewTrack = true;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Create the fourth Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.45;
        ///     tpt.Latitude = -36.5;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Send the Tracklog to the GPS device
        ///     gps.SetTrackLog(trk);
        ///
        /// }
        /// </code>
        /// </example>
        public override bool SetTrackLog(Tracklog tracklog)
		{
            return SetTrackLog(tracklog, false);
		}
        /// <summary>
        /// The SplitTracklog parameter, when set to true allows a multi-segment
        /// Tracklog object to be transferred to the GPS as multiple Tracklogs.
        /// A setting of false ensures a multi-segment Tracklog object is transferred
        /// to the GPS device as a single track.
        /// </summary>
        /// <example>C#
        /// <code>
        /// public void SetTracklog()
        /// {
        ///     MagellanDevice gps = new MagellanDevice();
        ///
        ///     Tracklog trk;
        ///     Trackpoint tpt;
        ///
        ///     //Create a new Tracklog object
        ///     trk = new Tracklog();
        ///     trk.Identifier = "Upload";
        ///
        ///     //Create the first Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.42;
        ///     tpt.Latitude = -36.47;
        ///     tpt.NewTrack = true;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Create a second Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.43;
        ///     tpt.Latitude = -36.48;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Create the third Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.44;
        ///     tpt.Latitude = -36.49;
        ///     tpt.NewTrack = true;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Create the fourth Trackpoint
        ///     tpt = new Trackpoint();
        ///     tpt.Longitude = 146.45;
        ///     tpt.Latitude = -36.5;
        ///     trk.Trackpoints.Add(tpt);
        ///
        ///     //Send the Tracklog to the GPS device
        ///     gps.SetTrackLog(trk);
        ///
        /// }
        /// </code>
        /// </example>
        public override bool SetTrackLog(Tracklog tracklog, bool splitTracklog)
        {
            bool result = false;
            int trackpointCount = 0;
            int totalTrackpoints = 0;

            try
            {
                PortOpen();

                totalTrackpoints = tracklog.Trackpoints.Count;

                foreach (Trackpoint objTrackpointpoint in tracklog.Trackpoints)
                {

                    trackpointCount++;

                    SentencePmgnTrk objSentence = new SentencePmgnTrk();

                    //copy the incomming waypoint to the new sentence
                    objSentence.Latitude = objTrackpointpoint.Latitude;
                    objSentence.Longitude = objTrackpointpoint.Longitude;
                    objSentence.Altitude = objTrackpointpoint.Altitude;
                    objSentence.AltitudeUnits = "M";
                    objSentence.UtcTime = objTrackpointpoint.FixDateTime.ToUniversalTime();

                    objSentence.Name = CharacterSetTrim(objTrackpointpoint.Name);
                    objSentence.UtcDate = objTrackpointpoint.FixDateTime;

                    string temp = objSentence.ToString();
                    SentencePmgnTrk objTemp = new SentencePmgnTrk(temp);

                    TransmitData(objSentence);

                    //work out percentage done and raise event for the caller
                    if (Progress != null)
                    {
                        //update the progress bar assume thirty waypoints
                        //as we have no way of telling
                        Progress(this, new ProgressEventArgs(Limit((int)(((100.0 / totalTrackpoints) * trackpointCount)), 0, 100)));
                    }

                }
                //work out percentage done and raise event for the caller
                if (Progress != null)
                {
                    //update the progress bar assume thirty waypoints
                    //as we have no way of telling
                    Progress(this, new ProgressEventArgs(100));
                }

                CommandEnd();

                //if we get here all must be ok
                result = true;

            }
            catch (Exception ex)
            {
                TraceLog.WriteError(ex);
                throw new DataTransferException(ex.Message, ex);
            }
            finally
            {
                PortClose();

                //work out percentage done and raise event for the caller
                if (Progress != null)
                {
                    //update the progress bar assume thirty waypoints
                    //as we have no way of telling
                    Progress(this, new ProgressEventArgs(0));
                }
            }
            return result;

        }

		#endregion

		#region Private Methods

        /// <summary>
        /// Re-dimensions an array without losing its contents.
        /// </summary>
        /// <param name="Array">Array to be re-dimensioned.</param>
        /// <param name="NewDimension">New dimension, representing the number of elements
        /// e.g. a number of 4 would redimension the array to have 4 elements (0-3).</param>
        /// <returns></returns>
        private SentencePmgnRte[] ReDimPreservePMGNRTE(SentencePmgnRte[] Array, int NewDimension)
        {
            //re-dimensions a byte array and preserves the contents
            if (NewDimension <= 0 || NewDimension == 0)
                throw new ArgumentException(ERR_MSG_3011);

            SentencePmgnRte[] newArray = new SentencePmgnRte[NewDimension];

            //copy any existing data
            if (Array != null)
                System.Array.Copy(Array, 0, newArray, 0, Array.Length);

            return newArray;
        }

        private Waypoint FindWaypoint(WaypointCollection Waypoints, string Identifier)
        {
            //add rest of waypoints
            Waypoint objWaypoint = Waypoints.Find(Identifier);

            if (objWaypoint != null)
            {
                //waypoint found so add it to the collectoin
                return objWaypoint;
            }
            else
            {
                //waypoint not found so could be an internal city etc
                //used in a route, so add an empty waypoint
                Waypoint objNewWaypoint = new Waypoint();
                objNewWaypoint.Identifier = Identifier;
                return objNewWaypoint;
            }
        }

		private string CharacterSetTrim(string Data)
		{
			return CharacterSetTrim(Data, 0);
		}

		private string CharacterSetTrim(string Data, int Length)
		{
			int iChar = 0;
			string sOut = "";

			if(Data.Length > 0)
			{
				//upper case letters and numbers
				Data = Data.ToUpper(CultureInfo.InvariantCulture);
				for(int f = 1; f <= Data.Length; f++)
				{
					iChar = Microsoft.VisualBasic.Strings.Asc(Data.Substring(f - 1, 1));
					if(iChar >= 32 && iChar <= 96)
						sOut = string.Concat(sOut, Microsoft.VisualBasic.Strings.Chr(iChar));
				}						
			}

			//remove commas as this screws up the NMEA Sentences
			sOut = sOut.Replace(",", "");
			if( Length > 0 && sOut.Length > 6 )
				sOut = sOut.Substring(0, Length);

			return sOut;
		}

		private void TransmitData(Sentence Sentence)
		{
			
			try
			{
				//TraceLog.WriteMessage("Receive Data	 : " + strMessage.Substring(0, strMessage.Length - 2), System.Diagnostics.TraceLevel.Verbose ,true);
				TraceLog.WriteMessage("Transmit Data : " + Sentence.ToString(), System.Diagnostics.TraceLevel.Verbose ,true);
				string sentence = Sentence.ToString() + "\r\n";
				comPort.Send(StringToByte(sentence));
			}
			catch(Exception ex)
			{
				throw ex;
			}
		}
		private Sentence ReceiveData()
		{
			Sentence objSentence = null;
			string sentence = m_buffer.Remove(BUFFER_READ_TIMEOUT);
            if (sentence != null)
            {
                if (sentence.Length > 0)
                {
                    objSentence = new Sentence(sentence);
                }
            }
            else
            {
                throw new ApplicationException(ERR_MSG_3012);
            }
			return objSentence;
		}
		/// <summary>
		/// Closes the currently active Serial Port.
		/// </summary>
		private void PortClose()
		{
			try
			{
				if(comPort.Online)
				{
					comPort.Close();
				}
			}
			catch(Exception ex)
			{
				throw ex;
			}
		}
		/// <summary>
		/// Opens the Serial Port (Comm Port) at the specified baud rate.
		/// </summary>
		private void PortOpen()
		{
			Sentence objSentence = null;
			
			try
			{
				if(m_port == 0)
				{
					throw new PortException(ERR_MSG_3010);
				}

				comPort.Close();

				comPort.Port = m_port;
				comPort.BaudRate = m_speed;

                //open the port, try three times as occasionally windows doesn't release
                //it as quickly as it might from the previous close
                int tries = 0;
                while (tries < 3)
                {
                    //unfortunate that we cant do this some other way
                    try
                    {
                        comPort.Open();
                        break;
                    }
                    catch (Exception ex)
                    {
                        tries++;
                        if (tries == 3)
                            throw ex;
                        else
                            //Console.WriteLine("Thread.Sleep(100)");
                            System.Threading.Thread.Sleep(100);
                    }
                }

				CommandHandOff();
				CommandNmeaOff();
				CommandTOn();

				//TOn causes an CMD,END message to be returned, by retrieving this here#
				//we can remove it when it arrives if we dont do this the buffer.clear command
				//in the following GetProductData method could clear the buffer before the
				//CMD,END arrives leaving a CMD,END in the buffer
				objSentence = ReceiveData();

				//m_productInfo = GetProductInfo();

//				TraceLog.WriteMessage("Product ID:          " + m_productInfo.ProductId.ToString(CultureInfo.InvariantCulture));
//				TraceLog.WriteMessage("Product Description: " + m_productInfo.ProductDescription);
//				TraceLog.WriteMessage("Software Version:    " + m_productInfo.SoftwareVersion);

			}
			catch
			{
				throw;
			}
		}

        /// <summary>
        /// Closes all resources and disposes of the object. 
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
        }
        /// <summary>
        /// Returns a SymbolId for a given symbol value. This function takes account of the various
        /// symbol sets in use by devices.
        /// </summary>
        /// <param name="symbolValue"></param>
        /// <returns></returns>
        private SymbolId GetSymbolId(string symbolValue)
        {
            SymbolId result = SymbolId.Unknown;

            //this is a long winded way to do this but makes for easier maintenance
            switch (m_prodInfo.SymbolSet)
            {
                case 0:
                    switch (symbolValue.ToLower())
                    {
                        case "a":
                            result = SymbolId.CircleFilled;
                            break;
                        case "b":
                            result = SymbolId.Box;
                            break;
                        case "c":
                            result = SymbolId.BuoyRed;
                            break;
                        case "d":
                            result = SymbolId.BuoyGreen;
                            break;
                        case "e":
                            result = SymbolId.Anchor;
                            break;
                        case "f":
                            result = SymbolId.Rocks;
                            break;
                        case "g":
                            result = SymbolId.DaymarkGreen;
                            break;
                        case "h":
                            result = SymbolId.DaymarkRed;
                            break;
                        case "i":
                            result = SymbolId.Bell;
                            break;
                        case "j":
                            result = SymbolId.Danger;
                            break;
                        case "k":
                            result = SymbolId.DiverDown;
                            break;
                        case "l":
                            result = SymbolId.Fish;
                            break;
                        case "m":
                            result = SymbolId.House;
                            break;
                        case "n":
                            result = SymbolId.Mark;
                            break;
                        case "o":
                            result = SymbolId.Car;
                            break;
                        case "p":
                            result = SymbolId.Tent;
                            break;
                        case "q":
                            result = SymbolId.Boat;
                            break;
                        case "r":
                            result = SymbolId.Food;
                            break;
                        case "s":
                            result = SymbolId.Fuel;
                            break;
                        case "t":
                            result = SymbolId.Tree;
                            break;
                    }
                    break;
                case 1:
                    switch (symbolValue.ToLower())
                    {
                        case "a":
                            result = SymbolId.CircleFilled;
                            break;
                        case "b":
                            result = SymbolId.Fish;
                            break;
                        case "c":
                            result = SymbolId.Buoy;
                            break;
                        case "d":
                            result = SymbolId.Light;
                            break;
                        case "e":
                            result = SymbolId.Anchor;
                            break;
                        case "f":
                            result = SymbolId.Flag;
                            break;
                        case "g":
                            result = SymbolId.DaymarkRed;
                            break;
                        case "h":
                            result = SymbolId.DaymarkGreen;
                            break;
                        case "i":
                            result = SymbolId.Wreck;
                            break;
                        case "j":
                            result = SymbolId.House;
                            break;
                        case "k":
                            result = SymbolId.Boat;
                            break;
                        case "l":
                            result = SymbolId.Fuel;
                            break;
                        case "m":
                            result = SymbolId.Danger;
                            break;
                        case "n":
                            result = SymbolId.DiverDown;
                            break;
                        case "o":
                            result = SymbolId.Food;
                            break;
                        case "p":
                            result = SymbolId.W;
                            break;
                        case "q":
                            result = SymbolId.L;
                            break;
                        case "r":
                            result = SymbolId.R;
                            break;
                    }
                    break;
                case 2:
                    switch (symbolValue.ToLower())
                    {
                        case "a":
                            result = SymbolId.CircleFilled;
                            break;
                        case "b":
                            result = SymbolId.FlagRight;
                            break;
                        case "c":
                            result = SymbolId.FlagLeft;
                            break;
                        case "d":
                            result = SymbolId.Diamond;
                            break;
                        case "e":
                            result = SymbolId.Square;
                            break;
                        case "f":
                            result = SymbolId.SquareFilled;
                            break;
                        case "g":
                            result = SymbolId.Anchor;
                            break;
                        case "h":
                            result = SymbolId.Banner;
                            break;
                        case "i":
                            result = SymbolId.Fish;
                            break;
                        case "j":
                            result = SymbolId.Crosshair;
                            break;
                    } break;
                case 3:
                    switch (symbolValue.ToLower())
                    {
                        case "a":
                            result = SymbolId.CircleFilled;
                            break;
                        case "b":
                            result = SymbolId.FlagRight;
                            break;
                        case "c":
                            result = SymbolId.FlagLeft;
                            break;
                        case "d":
                            result = SymbolId.Diamond;
                            break;
                        case "e":
                            result = SymbolId.Square;
                            break;
                        case "f":
                            result = SymbolId.SquareFilled;
                            break;
                        case "g":
                            result = SymbolId.Anchor;
                            break;
                        case "h":
                            result = SymbolId.Banner;
                            break;
                        case "i":
                            result = SymbolId.Fish;
                            break;
                        case "j":
                            result = SymbolId.Crosshair;
                            break;
                        case "k":
                            result = SymbolId.Car;
                            break;
                        case "l":
                            result = SymbolId.House;
                            break;
                        case "m":
                            result = SymbolId.WineGlass;
                            break;
                        case "n":
                            result = SymbolId.Mountain;
                            break;
                        case "o":
                            result = SymbolId.Sign;
                            break;
                        case "p":
                            result = SymbolId.Tree;
                            break;
                        case "q":
                            result = SymbolId.Water;
                            break;
                        case "r":
                            result = SymbolId.Airplane;
                            break;
                        case "s":
                            result = SymbolId.DeersHead;
                            break;
                    } 
                    break;
                case 4:
                    switch (symbolValue.ToLower())
                    {
                        case "a":
                            result = SymbolId.SquareCrossed;
                            break;
                        case "b":
                            result = SymbolId.Box;
                            break;
                        case "c":
                            result = SymbolId.House;
                            break;
                        case "d":
                            result = SymbolId.Aerial;
                            break;
                        case "e":
                            result = SymbolId.Airport;
                            break;
                        case "f":
                            result = SymbolId.AmusementPark;
                            break;
                        case "g":
                            result = SymbolId.Atm;
                            break;
                        case "h":
                            result = SymbolId.AutoRepair;
                            break;
                        case "i":
                            result = SymbolId.Boating;
                            break;
                        case "j":
                            result = SymbolId.Camping;
                            break;
                        case "k":
                            result = SymbolId.ExitRamp;
                            break;
                        case "l":
                            result = SymbolId.FirstAid;
                            break;
                        case "m":
                            result = SymbolId.NavAid;
                            break;
                        case "n":
                            result = SymbolId.Buoy;
                            break;
                        case "o":
                            result = SymbolId.Fuel;
                            break;
                        case "p":
                            result = SymbolId.Garden;
                            break;
                        case "q":
                            result = SymbolId.Golf;
                            break;
                        case "r":
                            result = SymbolId.Hotel;
                            break;
                        case "s":
                            result = SymbolId.HuntingFishing;
                            break;
                        case "t":
                            result = SymbolId.LargeCity;
                            break;
                        case "u":
                            result = SymbolId.Lighthouse;
                            break;
                        case "v":
                            result = SymbolId.MajorCity;
                            break;
                        case "w":
                            result = SymbolId.Marina;
                            break;
                        case "x":
                            result = SymbolId.MediumCity;
                            break;
                        case "y":
                            result = SymbolId.Museum;
                            break;
                        case "z":
                            result = SymbolId.Obstruction;
                            break;
                        case "aa":
                            result = SymbolId.Park;
                            break;
                        case "ab":
                            result = SymbolId.Resort;
                            break;
                        case "ac":
                            result = SymbolId.Restaurant;
                            break;
                        case "ad":
                            result = SymbolId.Rock;
                            break;
                        case "ae":
                            result = SymbolId.Scuba;
                            break;
                        case "af":
                            result = SymbolId.RvService;
                            break;
                        case "ag":
                            result = SymbolId.Shooting;
                            break;
                        case "ah":
                            result = SymbolId.SightSeeing;
                            break;
                        case "ai":
                            result = SymbolId.SmallCity;
                            break;
                        case "aj":
                            result = SymbolId.Sounding;
                            break;
                        case "ak":
                            result = SymbolId.SportsArena;
                            break;
                        case "al":
                            result = SymbolId.TouristInfo;
                            break;
                        case "am":
                            result = SymbolId.TruckService;
                            break;
                        case "an":
                            result = SymbolId.Winery;
                            break;
                        case "ao":
                            result = SymbolId.Wreck;
                            break;
                        case "ap":
                            result = SymbolId.Zoo;
                            break;
                    }
                    break;
            }

            return result;
        }
        /// <summary>
        /// Returns a given symbol value for a given SymbolId this function takes account of the 
        /// various symbol sets etc in use.
        /// </summary>
        /// <param name="symbolId"></param>
        /// <returns></returns>
        private string GetSymbolValue(SymbolId symbolId)
        {
            string result = "";

            //this is a long winded way to do this but makes for easier maintenance
            switch (m_prodInfo.SymbolSet)
            {
                case 0:
                    switch (symbolId)
                    {
                        case SymbolId.CircleFilled:
                            result = "a";
                            break;
                        case SymbolId.Box:
                            result = "b";
                            break;
                        case SymbolId.BuoyRed:
                            result = "c";
                            break;
                        case SymbolId.BuoyGreen:
                            result = "d";
                            break;
                        case SymbolId.Anchor:
                            result = "e";
                            break;
                        case SymbolId.Rocks:
                            result = "f";
                            break;
                        case SymbolId.DaymarkGreen:
                            result = "g";
                            break;
                        case SymbolId.DaymarkRed:
                            result = "h";
                            break;
                        case SymbolId.Bell:
                            result = "i";
                            break;
                        case SymbolId.Danger:
                            result = "j";
                            break;
                        case SymbolId.DiverDown:
                            result = "k";
                            break;
                        case SymbolId.Fish:
                            result = "l";
                            break;
                        case SymbolId.House:
                            result = "m";
                            break;
                        case SymbolId.Mark:
                            result = "n";
                            break;
                        case SymbolId.Car:
                            result = "o";
                            break;
                        case SymbolId.Tent:
                            result = "p";
                            break;
                        case SymbolId.Boat:
                            result = "q";
                            break;
                        case SymbolId.Food:
                            result = "r";
                            break;
                        case SymbolId.Fuel:
                            result = "s";
                            break;
                        case SymbolId.Tree:
                            result = "t";
                            break;
                    }
                    break;
                case 1:
                    switch (symbolId)
                    {
                        case SymbolId.CircleFilled:
                            result = "a";
                            break;
                        case SymbolId.Fish:
                            result = "b";
                            break;
                        case SymbolId.Buoy:
                            result = "c";
                            break;
                        case SymbolId.Light:
                            result = "d";
                            break;
                        case SymbolId.Anchor:
                            result = "e";
                            break;
                        case SymbolId.Flag:
                            result = "f";
                            break;
                        case SymbolId.DaymarkRed:
                            result = "g";
                            break;
                        case SymbolId.DaymarkGreen:
                            result = "h";
                            break;
                        case SymbolId.Wreck:
                            result = "i";
                            break;
                        case SymbolId.House:
                            result = "j";
                            break;
                        case SymbolId.Boat:
                            result = "k";
                            break;
                        case SymbolId.Fuel:
                            result = "l";
                            break;
                        case SymbolId.Danger:
                            result = "m";
                            break;
                        case SymbolId.DiverDown:
                            result = "n";
                            break;
                        case SymbolId.Food:
                            result = "o";
                            break;
                        case SymbolId.W:
                            result = "p";
                            break;
                        case SymbolId.L:
                            result = "q";
                            break;
                        case SymbolId.R:
                            result = "r";
                            break;
                    }
                    break;
                case 2:
                    switch (symbolId)
                    {
                        case SymbolId.CircleFilled:
                            result = "a";
                            break;
                        case SymbolId.FlagRight:
                            result = "b";
                            break;
                        case SymbolId.FlagLeft:
                            result = "c";
                            break;
                        case SymbolId.Diamond:
                            result = "d";
                            break;
                        case SymbolId.Square:
                            result = "e";
                            break;
                        case SymbolId.SquareFilled:
                            result = "f";
                            break;
                        case SymbolId.Anchor:
                            result = "g";
                            break;
                        case SymbolId.Banner:
                            result = "h";
                            break;
                        case SymbolId.Fish:
                            result = "i";
                            break;
                        case SymbolId.Crosshair:
                            result = "j";
                            break;
                    } break;
                case 3:
                    switch (symbolId)
                    {
                        case SymbolId.CircleFilled:
                            result = "a";
                            break;
                        case SymbolId.FlagRight:
                            result = "b";
                            break;
                        case SymbolId.FlagLeft:
                            result = "c";
                            break;
                        case SymbolId.Diamond:
                            result = "d";
                            break;
                        case SymbolId.Square:
                            result = "e";
                            break;
                        case SymbolId.SquareFilled:
                            result = "f";
                            break;
                        case SymbolId.Anchor:
                            result = "g";
                            break;
                        case SymbolId.Banner:
                            result = "h";
                            break;
                        case SymbolId.Fish:
                            result = "i";
                            break;
                        case SymbolId.Crosshair:
                            result = "j";
                            break;
                        case SymbolId.Car:
                            result = "k";
                            break;
                        case SymbolId.House:
                            result = "l";
                            break;
                        case SymbolId.WineGlass:
                            result = "m";
                            break;
                        case SymbolId.Mountain:
                            result = "n";
                            break;
                        case SymbolId.Sign:
                            result = "o";
                            break;
                        case SymbolId.Tree:
                            result = "p";
                            break;
                        case SymbolId.Water:
                            result = "q";
                            break;
                        case SymbolId.Airplane:
                            result = "r";
                            break;
                        case SymbolId.DeersHead:
                            result = "s";
                            break;
                    }
                    break;
                case 4:
                    switch (symbolId)
                    {
                        case SymbolId.SquareCrossed:
                            result = "a";
                            break;
                        case SymbolId.Box:
                            result = "b";
                            break;
                        case SymbolId.House:
                            result = "c";
                            break;
                        case SymbolId.Aerial:
                            result = "d";
                            break;
                        case SymbolId.Airport:
                            result = "e";
                            break;
                        case SymbolId.AmusementPark:
                            result = "f";
                            break;
                        case SymbolId.Atm:
                            result = "g";
                            break;
                        case SymbolId.AutoRepair:
                            result = "h";
                            break;
                        case SymbolId.Boating:
                            result = "i";
                            break;
                        case SymbolId.Camping:
                            result = "j";
                            break;
                        case SymbolId.ExitRamp:
                            result = "k";
                            break;
                        case SymbolId.FirstAid:
                            result = "l";
                            break;
                        case SymbolId.NavAid:
                            result = "m";
                            break;
                        case SymbolId.Buoy:
                            result = "n";
                            break;
                        case SymbolId.Fuel:
                            result = "o";
                            break;
                        case SymbolId.Garden:
                            result = "p";
                            break;
                        case SymbolId.Golf:
                            result = "q";
                            break;
                        case SymbolId.Hotel:
                            result = "r";
                            break;
                        case SymbolId.HuntingFishing:
                            result = "s";
                            break;
                        case SymbolId.LargeCity:
                            result = "t";
                            break;
                        case SymbolId.Lighthouse:
                            result = "u";
                            break;
                        case SymbolId.MajorCity:
                            result = "v";
                            break;
                        case SymbolId.Marina:
                            result = "w";
                            break;
                        case SymbolId.MediumCity:
                            result = "x";
                            break;
                        case SymbolId.Museum:
                            result = "y";
                            break;
                        case SymbolId.Obstruction:
                            result = "z";
                            break;
                        case SymbolId.Park:
                            result = "aa";
                            break;
                        case SymbolId.Resort:
                            result = "ab";
                            break;
                        case SymbolId.Restaurant:
                            result = "ac";
                            break;
                        case SymbolId.Rock:
                            result = "ad";
                            break;
                        case SymbolId.Scuba:
                            result = "ae";
                            break;
                        case SymbolId.RvService:
                            result = "af";
                            break;
                        case SymbolId.Shooting:
                            result = "ag";
                            break;
                        case SymbolId.SightSeeing:
                            result = "ah";
                            break;
                        case SymbolId.SmallCity:
                            result = "ai";
                            break;
                        case SymbolId.Sounding:
                            result = "aj";
                            break;
                        case SymbolId.SportsArena:
                            result = "ak";
                            break;
                        case SymbolId.TouristInfo:
                            result = "al";
                            break;
                        case SymbolId.TruckService:
                            result = "am";
                            break;
                        case SymbolId.Winery:
                            result = "an";
                            break;
                        case SymbolId.Wreck:
                            result = "ao";
                            break;
                        case SymbolId.Zoo:
                            result = "ap";
                            break;
                    }
                    break;
            }

            return result;

        }

		#endregion

		#region Internal Methods

        internal void ComPort_OnRxSentence(object source, OnRxSentenceEventArgs eargs)
        {
            string strMessage = "";
            Sentence objSentence = null;

            //write the line, remove the trailing CR first
            strMessage = eargs.RxSentence.ToString(CultureInfo.InvariantCulture);

            TraceLog.WriteMessage("Receive Data  : " + strMessage.Substring(0, strMessage.Length - 2), System.Diagnostics.TraceLevel.Verbose, true);

            //this could return an error if garbage is received
            objSentence = new Sentence(eargs.RxSentence);

            if (strMessage != null)
            {
                if (objSentence.ManufacturerId == MAGELLAN_MANUFACTURER_ID)
                {
                    m_buffer.Add(objSentence.ToString());
                }
            }
        }


		#endregion
	}
}
